fix(parser): allow TABLE/PARTITION/TABLES as identifiers in ClickHouse (#480)#481
fix(parser): allow TABLE/PARTITION/TABLES as identifiers in ClickHouse (#480)#481ajitpratap0 merged 11 commits intomainfrom
Conversation
Add support for SQL Server and Oracle PIVOT/UNPIVOT operators in FROM clauses. PIVOT transforms rows to columns via an aggregate function, while UNPIVOT performs the reverse column-to-row transformation. - Add PivotClause and UnpivotClause AST node types - Add Pivot/Unpivot fields to TableReference struct - Implement parsePivotClause/parseUnpivotClause in new pivot.go - Wire parsing into parseFromTableReference and parseJoinedTableRef - Add PIVOT/UNPIVOT to tokenizer keyword map for correct token typing - Update formatter to render PIVOT/UNPIVOT clauses - Enable testdata/mssql/11_pivot.sql and 12_unpivot.sql - Add 4 dedicated tests covering subquery+alias, plain table, AS alias Closes #456 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CVE-2026-32285 affects github.com/buger/jsonparser v1.1.1, which is a transitive dependency via mark3labs/mcp-go → invopop/jsonschema → wk8/go-ordered-map → buger/jsonparser. No fixed version is available upstream. The package is not called directly by any GoSQLX code and risk is scoped to MCP JSON schema generation. Added to .trivyignore until a patched version is released. Fixes Trivy Repository Scan CI failures in PR #475 and #477.
PIVOT and UNPIVOT were registered in the global tokenizer keyword map, which made them reserved words across every dialect. Queries like `SELECT pivot FROM users` then failed in PostgreSQL/MySQL/SQLite/ ClickHouse where these identifiers are perfectly legal. Changes: - Remove PIVOT/UNPIVOT from tokenizer keywordTokenTypes (they are now tokenized as identifiers in all dialects). - Gate isPivotKeyword/isUnpivotKeyword on the parser dialect (TSQL/ Oracle only) and accept identifier-typed tokens by value match. - Skip alias consumption in parseFromTableReference / parseJoinedTableRef when the upcoming identifier is a contextual PIVOT/UNPIVOT keyword, so the pivot-clause parser can claim it. - Fix unsafe single-value type assertion in TestTSQL_PivotWithASAlias to comply with the project's mandatory two-value form. - Add TestPivotIdentifierInNonTSQLDialects regression covering pivot/ unpivot as identifiers in PostgreSQL, MySQL, and SQLite. All parser/tokenizer/formatter tests pass with -race.
- Formatter emits AS before PIVOT/UNPIVOT aliases for clean round-trip. - Tokenizer records Quote='[' on SQL Server bracket-quoted identifiers; pivot parser uses renderQuotedIdent to preserve [North] etc. in PivotClause.InValues and UnpivotClause.InColumns. - Reject empty IN lists for both PIVOT and UNPIVOT. - Extract parsePivotAlias helper, collapsing four duplicated alias blocks in select_subquery.go. - Add TestPivotNegativeCases (missing parens, missing FOR/IN, empty IN) and TestPivotBracketedInValuesPreserved. Full test suite passes with -race.
- renderQuotedIdent now doubles embedded `]`, `"`, and `` ` `` per dialect convention so identifiers like [foo]bar] round-trip unambiguously. - Empty PIVOT/UNPIVOT IN list check now runs before the closing-`)` check so the user-facing error is "at least one value/column..." instead of the misleading ") to close ... IN list". - Clarify renderQuotedIdent comment to reference Token.Quote and Word.QuoteStyle as the actual sources.
…nSQL Previously these package-level renderers hardcoded keyword literals (PIVOT, UNPIVOT, FOR, IN, LATERAL, JOIN, ON) which bypassed the caller's case policy (f.kw). Thread *nodeFormatter into both functions and route every keyword through f.kw so uppercase/lowercase options apply uniformly across FROM, JOIN, MERGE, DELETE USING, and UPDATE FROM paths. Addresses claude-review feedback on PR #477. All formatter, parser, and tokenizer tests pass with -race.
#480) ClickHouse system tables (system.replicas, system.parts, system.tables) expose columns named `table` and `partition`, and queries commonly reference `system.tables`. The parser previously rejected these because TABLE, PARTITION, and TABLES tokenize as keywords and the non-reserved-keyword-as-identifier path was gated to SQL Server only. - Extend the gate in parsePrimaryExpression to also enable for ClickHouse. - Add TokenTypePartition and the "TABLES" keyword value to isNonReservedKeyword so they can serve as identifiers in expression position and after qualifiers. Closes #480
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @ajitpratap0's task —— View job Code Review Complete ✅Reviewing PR #481: ClickHouse table identifier fix Todo List:
Code Quality Assessment✅ Strengths:
|
|
Claude finished @ajitpratap0's task —— View job Code Review Complete ✅Reviewing PR #481: ClickHouse table identifier fix Todo List:
Code Quality Assessment✅ Strengths:
📊 Technical Analysis:Changes Made:
Scope Assessment:
🔒 Security Assessment: ✅ CLEAN
⚡ Performance Assessment: ✅ MINIMAL IMPACT
📝 Code Quality: ✅ EXCELLENT
🎯 Test Coverage: ✅ COMPREHENSIVE
|
Extend the non-reserved-keyword allowlist to also accept ROWS as a column identifier. ClickHouse exposes a `rows` column in system.tables and other system tables; ROWS is otherwise reserved for window-frame syntax (ROWS BETWEEN ...) but is unambiguous in expression position. Part of #482 (ClickHouse parser gaps from QA sweep).
|
Claude finished @ajitpratap0's task —— View job Code Review Complete ✅Reviewing PR #481: ClickHouse table identifier fix Todo List:
Code Quality Assessment✅ Strengths:
📊 Technical Analysis:Changes Made:
Scope Assessment:
🔒 Security Assessment: ✅ CLEAN
⚡ Performance Assessment: ✅ MINIMAL IMPACT
📝 Code Quality: ✅ EXCELLENT
🎯 Test Coverage: ✅ COMPREHENSIVE
|
Summary
table,partition, orsystem.tablesas identifiers now parse correctly.system.replicas,system.parts,system.tables.TokenTypePartitionand the"TABLES"keyword value toisNonReservedKeyword.Test plan
pkg/sql/parser/clickhouse_table_identifier_test.gocovering all 5 queries from the issuego test -race ./pkg/sql/parser/ ./pkg/sql/keywords/ ./pkg/gosqlx/— all greentask check(full suite) — all greenCREATE TABLE,ALTER TABLE) — TABLE in DDL position is consumed before reaching expression context🤖 Generated with Claude Code